/**
 **************************************************************************
 * @file     at32f403a_407_board.c
 * @version  v2.0.7
 * @date     2022-02-11
 * @brief    set of firmware functions to manage leds and push-button.
 *           initialize delay function.
 **************************************************************************
 *                       Copyright notice & Disclaimer
 *
 * The software Board Support Package (BSP) that is made available to
 * download from Artery official website is the copyrighted work of Artery.
 * Artery authorizes customers to use, copy, and distribute the BSP
 * software and its related documentation for the purpose of design and
 * development in conjunction with Artery microcontrollers. Use of the
 * software is governed by this copyright notice and the following disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
 * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
 * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
 * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
 * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
 *
 **************************************************************************
 */

#include "at32f403a_407_board.h"

/** @addtogroup AT32F403A_407_board
 * @{
 */

/** @defgroup BOARD
 * @brief onboard periph driver
 * @{
 */

/* delay macros */
#define STEP_DELAY_MS 50

/* at-start led resouce array */
gpio_type *led_gpio_port[LED_NUM] = {LED2_GPIO, LED3_GPIO, LED4_GPIO};
uint16_t led_gpio_pin[LED_NUM] = {LED2_PIN, LED3_PIN, LED4_PIN};
crm_periph_clock_type led_gpio_crm_clk[LED_NUM] = {LED2_GPIO_CRM_CLK, LED3_GPIO_CRM_CLK, LED4_GPIO_CRM_CLK};

/* delay variable */
static __IO uint32_t fac_us;
static __IO uint32_t fac_ms;
static __IO uint32_t dwt_fac_us;
static __IO uint32_t dwt_fac_ms;
/* support printf function, usemicrolib is unnecessary */
#if (__ARMCC_VERSION > 6000000)
__asm(".global __use_no_semihosting\n\t");
void _sys_exit(int x)
{
  x = x;
}
/* __use_no_semihosting was requested, but _ttywrch was */
void _ttywrch(int ch)
{
  ch = ch;
}
FILE __stdout;
#else
#ifdef __CC_ARM
#pragma import(__use_no_semihosting)
struct __FILE
{
  int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
  x = x;
}
/* __use_no_semihosting was requested, but _ttywrch was */
void _ttywrch(int ch)
{
  ch = ch;
}
#endif
#endif

#if defined(__GNUC__) && defined(__clang__)
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

/**
 * @brief  retargets the c library printf function to the usart.
 * @param  none
 * @retval none
 */
void Usart2_send_byte(uint8_t data);
void Usart2_send_data(uint8_t *data, uint32_t len);
PUTCHAR_PROTOTYPE
{
  // while (usart_flag_get(PRINT_UART, USART_TDBE_FLAG) == RESET)
  //   ;
  // usart_data_transmit(PRINT_UART, ch);
  Usart2_send_byte((uint8_t)(ch));
  return ch;
}

/**
 * @brief  initialize uart
 * @param  baudrate: uart baudrate
 * @retval none
 */
// void uart_print_init(uint32_t baudrate)
// {
//   gpio_init_type gpio_init_struct;

//   /* enable the uart and gpio clock */
//   crm_periph_clock_enable(PRINT_UART_CRM_CLK, AT_TRUE);
//   crm_periph_clock_enable(PRINT_UART_TX_GPIO_CRM_CLK, AT_TRUE);
//   crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);

//   gpio_default_para_init(&gpio_init_struct);

//   gpio_pin_remap_config(USART2_MUX, TRUE);

//   /* configure the uart tx pin */
//   gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
//   gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
//   gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
//   gpio_init_struct.gpio_pins = PRINT_UART_TX_PIN;
//   gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
//   gpio_init(PRINT_UART_TX_GPIO, &gpio_init_struct);

//   /* configure uart param */
//   usart_init(PRINT_UART, baudrate, USART_DATA_8BITS, USART_STOP_1_BIT);
//   usart_transmitter_enable(PRINT_UART, AT_TRUE);
//   usart_enable(PRINT_UART, AT_TRUE);
// }
#define RX_BUFFER_LEN 1024
static uint8_t rx_buffer[RX_BUFFER_LEN];
static uint32_t rx_cnt = 0;
void uart_print_init(uint32_t baudrate)
{
  gpio_init_type gpio_init_struct;

  /* enable the uart and gpio clock */
  crm_periph_clock_enable(PRINT_UART_CRM_CLK, AT_TRUE);         // 开启usart2时钟
  crm_periph_clock_enable(PRINT_UART_TX_GPIO_CRM_CLK, AT_TRUE); // 开启gpioD时钟
  crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);        // 开启重映射时钟
  crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);         // 开启DMA1的时钟

  /*************gpio conifg****************************/
  gpio_default_para_init(&gpio_init_struct);

  /* configure the uart tx pin */
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = PRINT_UART_TX_PIN;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(PRINT_UART_GPIO, &gpio_init_struct);

  /* configure the uart rx pin */
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = PRINT_UART_RX_PIN;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(PRINT_UART_GPIO, &gpio_init_struct);

  gpio_pin_remap_config(USART2_GMUX_0001, TRUE);

  /***********************dma conifg****************************/
  dma_init_type dma_init_struct;
  // tx
  dma_reset(DMA_CH_TX);
  dma_default_para_init(&dma_init_struct);
  // dma_init_struct.buffer_size = 0;                                        // 内存大小
  dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL; // 外设地址为目的地址
  // dma_init_struct.memory_base_addr = (uint32_t)0;                         // 内存地址
  dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;         // 内存数据的宽度
  dma_init_struct.memory_inc_enable = TRUE;                               // 内存地址递增打开
  dma_init_struct.peripheral_base_addr = (uint32_t)&USART2->dt;           // 外设地址
  dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE; // 外设数据的宽度
  dma_init_struct.peripheral_inc_enable = FALSE;                          // 外设地址递增关闭
  dma_init_struct.priority = DMA_PRIORITY_HIGH;                           // 高优先级
  dma_init_struct.loop_mode_enable = FALSE;                               // 不循环
  dma_init(DMA_CH_TX, &dma_init_struct);
  // rx
  dma_reset(DMA_CH_RX);
  dma_init_struct.peripheral_base_addr = (uint32_t)&USART2->dt; // 外DMA_CH_RX设地址
  dma_init_struct.memory_base_addr = (uint32_t)rx_buffer;       // 内存地址
  dma_init_struct.memory_inc_enable = TRUE;                     // 内存地址递增打开
  dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;     // 外设地址为源地址
  dma_init_struct.buffer_size = RX_BUFFER_LEN;
  dma_init_struct.peripheral_inc_enable = FALSE; // 外设地址递增
  dma_init(DMA_CH_RX, &dma_init_struct);

  dma_channel_enable(DMA_CH_RX, TRUE); // dma rx ch6
  // dma_channel_enable(DMA_CH_TX, TRUE); // dma tx ch7

  /***********************usart conifg****************************/
  usart_init(PRINT_UART, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
  usart_hardware_flow_control_set(PRINT_UART, USART_HARDWARE_FLOW_NONE); // 无硬件流
  usart_transmitter_enable(PRINT_UART, TRUE);                            // 使能串口
  usart_receiver_enable(PRINT_UART, TRUE);                               // 使能串口
  usart_parity_selection_config(PRINT_UART, USART_PARITY_NONE);          // 无校验
  usart_dma_transmitter_enable(PRINT_UART, TRUE);                        // 使能串口dma
  usart_dma_receiver_enable(PRINT_UART, TRUE);                           // 使能串口dma
  // nvic
  nvic_irq_enable(USART2_IRQn, 1, 0);                       // 使能串口2中断，优先级2，次优先级2
  usart_interrupt_enable(PRINT_UART, USART_RDBF_INT, TRUE); // 接收中断
  usart_interrupt_enable(PRINT_UART, USART_IDLE_INT, TRUE); // 使能空闲中断
  usart_enable(PRINT_UART, AT_TRUE);
}
/**
 * @brief  this function handles USART2 handler.
 * @param  none
 * @retval none
 */
void USART2_IRQHandler(void)
{
  if (usart_flag_get(PRINT_UART, USART_IDLEF_FLAG) != RESET)
  {
    usart_data_receive(PRINT_UART);               // 读操作 清空缓冲区
    usart_flag_clear(PRINT_UART, USART_IDLE_INT); // 清除IDLE中断

    dma_channel_enable(DMA_CH_RX, FALSE);
    dma_flag_clear(DMA1_FDT6_FLAG);
    rx_cnt = RX_BUFFER_LEN - dma_data_number_get(DMA_CH_RX);
    rx_buffer[rx_cnt] = '\0';
#if USART2_RECV_CALLBACK
    // printf("recv2:%s\n", rx_buffer);
    Usart2_on_recv(rx_buffer, rx_cnt);
#endif
    // 重置计数器
    DMA_CH_RX->dtcnt = RX_BUFFER_LEN;       // 接收的数据长度
    DMA_CH_RX->maddr = (uint32_t)rx_buffer; // 存放数据buf地址
    dma_channel_enable(DMA_CH_RX, TRUE);
    rx_cnt = 0;
  }
}
void Usart2_dma_channel_config(dma_channel_type *dmax_channely, uint32_t peripheral_base_addr, uint32_t memory_base_addr, uint16_t buffer_size)
{
  dmax_channely->dtcnt = buffer_size;
  dmax_channely->paddr = peripheral_base_addr;
  dmax_channely->maddr = memory_base_addr;
}
/**
 * USART2_send_data
 */
void Usart2_send_data(uint8_t *data, uint32_t len)
{
  Usart2_dma_channel_config(DMA_CH_TX, (uint32_t)&USART2->dt, (uint32_t)data, len);
  dma_channel_enable(DMA_CH_TX, TRUE);
  while (dma_flag_get(DMA1_FDT7_FLAG) == RESET)
    ;
  dma_flag_clear(DMA1_FDT7_FLAG);
  dma_channel_enable(DMA_CH_TX, FALSE);
}
void Usart2_send_byte(uint8_t data)
{
  Usart2_send_data(&data, 1);
}
/**
 * @brief  board initialize interface init led and button
 * @param  none
 * @retval none
 */
void at32_board_init()
{
  /* initialize delay function */
  delay_init();

  dwt_delay_init();
  /* configure led in at_start_board */
  // at32_led_init(LED2);
  // at32_led_init(LED3);
  // at32_led_init(LED4);
  // at32_led_on(LED2);
  // at32_led_off(LED3);
  // at32_led_off(LED4);

  /* configure button in at_start board */
  // at32_button_init();
}

/**
 * @brief  configure button gpio
 * @param  button: specifies the button to be configured.
 * @retval none
 */
void at32_button_init(void)
{
  gpio_init_type gpio_init_struct;

  /* enable the button clock */
  crm_periph_clock_enable(USER_BUTTON_CRM_CLK, AT_TRUE);

  /* set default parameter */
  gpio_default_para_init(&gpio_init_struct);

  /* configure button pin as input with pull-up/pull-down */
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
  gpio_init_struct.gpio_pins = USER_BUTTON_PIN;
  gpio_init_struct.gpio_pull = GPIO_PULL_DOWN;
  gpio_init(USER_BUTTON_PORT, &gpio_init_struct);
}

/**
 * @brief  returns the selected button state
 * @param  none
 * @retval the button gpio pin value
 */
uint8_t at32_button_state(void)
{
  return gpio_input_data_bit_read(USER_BUTTON_PORT, USER_BUTTON_PIN);
}

/**
 * @brief  returns which button have press down
 * @param  none
 * @retval the button have press down
 */
button_type at32_button_press()
{
  static uint8_t pressed = 1;
  /* get button state in at_start board */
  if ((pressed == 1) && (at32_button_state() != RESET))
  {
    /* debounce */
    pressed = 0;
    delay_ms(10);
    if (at32_button_state() != RESET)
      return USER_BUTTON;
  }
  else if (at32_button_state() == RESET)
  {
    pressed = 1;
  }
  return NO_BUTTON;
}

/**
 * @brief  configure led gpio
 * @param  led: specifies the led to be configured.
 * @retval none
 */
void at32_led_init(led_type led)
{
  gpio_init_type gpio_init_struct;

  /* enable the led clock */
  crm_periph_clock_enable(led_gpio_crm_clk[led], AT_TRUE);

  /* set default parameter */
  gpio_default_para_init(&gpio_init_struct);

  /* configure the led gpio */
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
  gpio_init_struct.gpio_pins = led_gpio_pin[led];
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(led_gpio_port[led], &gpio_init_struct);
}

/**
 * @brief  turns selected led on.
 * @param  led: specifies the led to be set on.
 *   this parameter can be one of following parameters:
 *     @arg LED2
 *     @arg LED3
 *     @arg LED4
 * @retval none
 */
void at32_led_on(led_type led)
{
  if (led > (LED_NUM - 1))
    return;
  if (led_gpio_pin[led])
    led_gpio_port[led]->clr = led_gpio_pin[led];
}

/**
 * @brief  turns selected led off.
 * @param  led: specifies the led to be set off.
 *   this parameter can be one of following parameters:
 *     @arg LED2
 *     @arg LED3
 *     @arg LED4
 * @retval none
 */
void at32_led_off(led_type led)
{
  if (led > (LED_NUM - 1))
    return;
  if (led_gpio_pin[led])
    led_gpio_port[led]->scr = led_gpio_pin[led];
}

/**
 * @brief  turns selected led tooggle.
 * @param  led: specifies the led to be set off.
 *   this parameter can be one of following parameters:
 *     @arg LED2
 *     @arg LED3
 *     @arg LED4
 * @retval none
 */
void at32_led_toggle(led_type led)
{
  if (led > (LED_NUM - 1))
    return;
  if (led_gpio_pin[led])
    led_gpio_port[led]->odt ^= led_gpio_pin[led];
}

/**
 * @brief  initialize delay function
 * @param  none
 * @retval none
 */
void delay_init()
{
  /* configure systick */
  systick_clock_source_config(SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV);
  fac_us = system_core_clock / (1000000U);
  // SysTick_Config();
  // fac_ms = fac_us * (1000U);
}

/**
 * @brief  inserts a delay time.
 * @param  nus: specifies the delay time length, in microsecond.
 * @retval none
 */
void delay_us(uint32_t nus)
{
  // fac_us = nus;
  // while (fac_us != 0U)
  // {
  //   /* code */
  // }
  uint32_t temp;
  SysTick->LOAD = 24 * nus;
  SysTick->VAL = 0X00;  // 清空计数器
  SysTick->CTRL = 0X01; // 使能，减到零是无动作，采用外部时钟源
  do
  {
    temp = SysTick->CTRL; // 读取当前倒计数值
  } while ((temp & 0x01) && (!(temp & (1 << 16)))); // 等待时间到达
  SysTick->CTRL = 0x00; // 关闭计数器
  SysTick->VAL = 0X00;  // 清空计数器
}

/**
 * @brief  inserts a delay time.
 * @param  nms: specifies the delay time length, in milliseconds.
 * @retval none
 */
void delay_ms(uint16_t nms)
{
  fac_us = nms * 1000;
  while (fac_us != 0U)
  {
    /* code */
  }
}

/**
 * @brief  inserts a delay time.
 * @param  sec: specifies the delay time, in seconds.
 * @retval none
 */
void delay_sec(uint16_t sec)
{
  fac_us = sec * 1000000;
  while (fac_us != 0U)
  {
    /* code */
  }
}
void delay_decrement(void)
{
  if (0U != fac_us)
  {
    fac_us--;
  }
}
/**
 * @brief  initialize dwt delay function
 * @param  none
 * @retval none
 */
void dwt_delay_init()
{
  crm_clocks_freq_type crm_clocks_freq_struct = {0};
  /* get system clock */
  crm_clocks_freq_get(&crm_clocks_freq_struct);

  /* configure dwt */
  dwt_fac_us = crm_clocks_freq_struct.sclk_freq / (1000000U);
  dwt_fac_ms = dwt_fac_us * (1000U);
}

/**
 * @brief  inserts a dwt delay time.
 * @param  nus: specifies the dwt delay time length, in microsecond.
 * @retval none
 */
void dwt_delay_us(uint32_t nus)
{
  /* Enable CoreDebug DEMCR bit24: TRCENA */
  CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
  /* Clear CYCCNT */
  DWT->CYCCNT = 0x00;
  /* Enable DWT CTRL bit0: CYCCNTENA */
  DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

  uint32_t temp = DWT->CYCCNT;
  while ((DWT->CYCCNT - temp) < nus * dwt_fac_us)
    ;

  /* Disable DWT CTRL bit0: CYCCNTENA */
  DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
  /* Clear CYCCNT */
  DWT->CYCCNT = 0x00;
  /* Disable CoreDebug DEMCR bit24: TRCENA */
  CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
}

/**
 * @brief  inserts a dwt delay time.
 * @param  nms: 0x0000~0x45EF, specifies the dwt delay time length, in milliseconds.
 * @retval none
 */
void dwt_delay_ms(uint16_t nms)
{
  /* Enable CoreDebug DEMCR bit24: TRCENA */
  CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
  /* Clear CYCCNT */
  DWT->CYCCNT = 0x00;
  /* Enable DWT CTRL bit0: CYCCNTENA */
  DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

  uint32_t temp = DWT->CYCCNT;
  while ((DWT->CYCCNT - temp) < nms * dwt_fac_ms)
    ;

  /* Disable DWT CTRL bit0: CYCCNTENA */
  DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
  /* Clear CYCCNT */
  DWT->CYCCNT = 0x00;
  /* Disable CoreDebug DEMCR bit24: TRCENA */
  CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
}

/**
 * @brief  inserts a dwt delay time.
 * @param  sec: specifies the dwt delay time, in seconds.
 * @retval none
 */
void dwt_delay_sec(uint16_t sec)
{
  uint16_t index;
  for (index = 0; index < sec; index++)
  {
    dwt_delay_ms(1000);
  }
}
